import { ByteUtils } from '../utils/ByteUtils'

class HmacSha1 {

    constructor() {
        this.MAX_MESSAGE_LENGTH = 4096
    }

    hmac_sha1(key, key_length, data, data_length) {
        let b = 64; /* blocksize */
        let ipad = 0x36;
        let opad = 0x5c;

        let digest = new Uint8Array(new ArrayBuffer(20));

        let k0 = new Uint8Array(new ArrayBuffer(64));
        let k0xorIpad = new Uint8Array(new ArrayBuffer(64));
        let step7data = new Uint8Array(new ArrayBuffer(64));
        let step5data = new Uint8Array(new ArrayBuffer(this.MAX_MESSAGE_LENGTH + 128));
        let step8data = new Uint8Array(new ArrayBuffer(64 + 20));
        let i;

        for (i = 0; i < 64; i++) {
            k0[i] = 0x00;
        }

        /* Step 1 */
        if (key_length !== b) {
            /* Step 2 */
            if (key_length > b) {
                this.sha1(key, key_length, digest);
                for (i = 0; i < 20; i++) {
                    k0[i] = digest[i];
                }
            } else if (key_length < b)  /* Step 3 */
            {
                for (i = 0; i < key_length; i++) {
                    k0[i] = key[i];
                }
            }
        } else {
            for (i = 0; i < b; i++) {
                k0[i] = key[i];
            }
        }
        /* Step 4 */
        for (i = 0; i < 64; i++) {
            k0xorIpad[i] = k0[i] ^ ipad;
        }
        // console.log("k0xorIpad = ",ByteUtils.bytesToHexStr(k0xorIpad));
        /* Step 5 */
        for (i = 0; i < 64; i++) {
            step5data[i] = k0xorIpad[i];
        }
        for (i = 0; i < data_length; i++) {
            step5data[i + 64] = data[i];
        }

        console.log("step5data = ",ByteUtils.bytesToHexStr(step5data.slice(0,72)));

        /* Step 6 */
        let s_digest = this.sha1(step5data.slice(0,72));
        digest = ByteUtils.hexStrToBytes(s_digest);

        /* Step 7 */
        for (i = 0; i < 64; i++) {
            step7data[i] = k0[i] ^ opad;
        }

        /* Step 8 */
        for (i = 0; i < 64; i++) {
            step8data[i] = step7data[i];
        }
        for (i = 0; i < 20; i++) {
            step8data[i + 64] = digest[i];
        }

        console.log("step8data = ",ByteUtils.bytesToHexStr(step8data.slice(0,84)));

        /* Step 9 */
        // this.sha1(step8data, b + 20, digest);
        return this.sha1(step8data.slice(0,84))
    }


    sha1(array) {

        console.log("sha1 param = "+ByteUtils.bytesToHexStr(array));

        // var data = new Uint8Array(encodeUTF8(s))
        var data = new Uint8Array(array);
        var i, j, t;
        var l = ((data.length + 8) >>> 6 << 4) + 16, s = new Uint8Array(l << 2);
        s.set(new Uint8Array(data.buffer)), s = new Uint32Array(s.buffer);
        for (t = new DataView(s.buffer), i = 0; i < l; i++)s[i] = t.getUint32(i << 2);
        s[data.length >> 2] |= 0x80 << (24 - (data.length & 3) * 8);
        s[l - 1] = data.length << 3;
        var w = [], f = [
                function () { return m[1] & m[2] | ~m[1] & m[3]; },
                function () { return m[1] ^ m[2] ^ m[3]; },
                function () { return m[1] & m[2] | m[1] & m[3] | m[2] & m[3]; },
                function () { return m[1] ^ m[2] ^ m[3]; }
            ], rol = function (n, c) { return n << c | n >>> (32 - c); },
            k = [1518500249, 1859775393, -1894007588, -899497514],
            m = [1732584193, -271733879, null, null, -1009589776];
        m[2] = ~m[0], m[3] = ~m[1];
        for (i = 0; i < s.length; i += 16) {
            var o = m.slice(0);
            for (j = 0; j < 80; j++)
                w[j] = j < 16 ? s[i + j] : rol(w[j - 3] ^ w[j - 8] ^ w[j - 14] ^ w[j - 16], 1),
                    t = rol(m[0], 5) + f[j / 20 | 0]() + m[4] + w[j] + k[j / 20 | 0] | 0,
                    m[1] = rol(m[1], 30), m.pop(), m.unshift(t);
            for (j = 0; j < 5; j++)m[j] = m[j] + o[j] | 0;
        };
        t = new DataView(new Uint32Array(m).buffer);
        for (var i = 0; i < 5; i++)m[i] = t.getUint32(i << 2);

        var hex = Array.prototype.map.call(new Uint8Array(new Uint32Array(m).buffer), function (e) {
            return (e < 16 ? "0" : "") + e.toString(16);
        }).join("");

        console.log("sha1 result = "+hex);
        return hex;
    };
}


const hmacSha1 = new HmacSha1();
export default hmacSha1
